/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.common.serialize;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.inspur.edp.web.common.logger.WebLogger;

import java.io.IOException;
import java.util.Objects;

/**
 * The type Serializer utility.
 *
 * @author wangxufa
 * @description Json序列化工具类
 */
public class SerializeUtility {
    // region 单例

    private final ObjectMapper defaultObjectMapper;

    private SerializeUtility() {
        defaultObjectMapper = this.createDefaultObjectMapper();
    }

    /**
     * 增加volatile的作用是为了避免多线程访问时，导致变量不一致问题
     */
    private volatile static SerializeUtility serializeUtilityInstance = null;
    private static final Object _lock = new Object();

    public static SerializeUtility getInstance() {
        if (Objects.isNull(serializeUtilityInstance)) {
            synchronized (_lock) {
                if (Objects.isNull(serializeUtilityInstance)) {
                    serializeUtilityInstance = new SerializeUtility();
                }
            }
        }

        return serializeUtilityInstance;
    }

    private ObjectMapper createDefaultObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());
        setDefaultConfiguration(mapper);
        return mapper;
    }

    public void setDefaultConfiguration(ObjectMapper mapper) {
        mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
        mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        mapper.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 大小写脱敏 默认为false  需要时可改为true
        mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);

    }

    // endregion

    // region默认配置

    /**
     * 获取默认ObjectMapper实例
     *
     * @return 返回默认ObjectMapper实例
     */
    public ObjectMapper getDefaultObjectMapper() {
        return this.defaultObjectMapper;
    }

    /**
     * Deserialize string to an object.
     * 将一个对象字符串反序列化成对象
     *
     * @param contentString the content string
     * @return the t
     * @description Json反序列化
     */
    public <T> T deserialize(String contentString, Class<?> valueType) {
        return deserialize(this.defaultObjectMapper, contentString, valueType);
    }

    public <T> T deserialize(String contentString, TypeReference<T> valueTypeRef) {
        try {
            return defaultObjectMapper.readValue(contentString, valueTypeRef);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * Serialize an object to string.
     * 保持属性名称一致
     *
     * @param entity the entity
     * @return the string instance
     * @description Json序列化
     */
    public String serialize(Object entity) {
        return serialize(entity, false);
    }

    /**
     * Serialize an object to string.
     * 保持属性名称一致
     *
     * @param entity the entity
     * @return the string instance
     * @description Json序列化
     */
    public String serialize(Object entity, boolean usePretty) {
        PropertyNamingStrategy currentNameStrategy = this.defaultObjectMapper.getPropertyNamingStrategy();
        this.defaultObjectMapper.setPropertyNamingStrategy(new MyPropertyNamingStrategy());
        String result = serialize(this.defaultObjectMapper, entity, usePretty);
        this.defaultObjectMapper.setPropertyNamingStrategy(currentNameStrategy);
        return result;
    }

    /**
     * Serialize an object to string.
     *
     * @param entity the entity
     * @return the string instance
     * @description Json序列化
     */
    public String serialize(Object entity, PropertyNamingStrategy strategy, boolean usePretty) {
        ObjectMapper mapper = this.getDefaultObjectMapper();
        PropertyNamingStrategy originalStrategy = mapper.getPropertyNamingStrategy();
        boolean changeStrategy = !originalStrategy.equals(strategy);
        if (changeStrategy) {
            mapper.setPropertyNamingStrategy(strategy);
        }

        String result = serialize(mapper, entity, usePretty);

        if (changeStrategy) {
            mapper.setPropertyNamingStrategy(originalStrategy);
        }
        return result;

    }

    /**
     * 将序列化对象转换成JsonNode
     *
     * @param objectContent
     * @return
     */
    public JsonNode readTree(String objectContent) {
        return readTree(this.defaultObjectMapper, objectContent);
    }

    public <T> T toObject(JsonNode jsonNode, Class valueType) {
        if (jsonNode == null) {
            return null;
        }
        String jsonNodeString = jsonNode.toString();
        return deserialize(jsonNodeString, valueType);

    }

    /**
     * 字符串转JsonNode
     *
     * @param source
     * @return
     */
    public JsonNode toJsonNode(String source) {
        return readTree(source);
    }


    // endregion

    // region 自定义配置

    /**
     * Deserialize string to an object.
     * 将一个对象字符串反序列化成对象
     *
     * @param objectMapper
     * @param contentString
     * @param valueType
     * @param <T>
     * @return
     * @description Json反序列化
     */
    public <T> T deserialize(ObjectMapper objectMapper, String contentString, Class valueType) {
        if (contentString == null || contentString.isEmpty()) {
            return null;
        }

        try {
            return (T) (objectMapper.readValue(contentString, valueType));
        } catch (IOException e) {
            WebLogger.Instance.error(e);
            return null;
        }
    }

    /**
     * Serialize an object to string.
     *
     * @param objectMapper the objectMapper
     * @param entity       the entity
     * @return the string instance
     * @description Json序列化
     */
    public String serialize(ObjectMapper objectMapper, Object entity, boolean usePretty) {
        if (entity == null) {
            return null;
        }

        String serializeStr = null;

        try {
            if (!usePretty) {
                serializeStr = objectMapper.writeValueAsString(entity);
            } else {
                serializeStr = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(entity);
            }

            return serializeStr;
        } catch (JsonProcessingException e) {
            WebLogger.Instance.error(e);
            return null;
        }
    }

    /**
     * 将序列化对象转换成JsonNode
     *
     * @param objectMapper
     * @param objectContent
     * @return
     */
    public JsonNode readTree(ObjectMapper objectMapper, String objectContent) {
        if (objectContent == null || objectContent.isEmpty()) {
            return null;
        }

        JsonNode objectJsonNode = null;
        try {
            objectJsonNode = objectMapper.readTree(objectContent);
            return objectJsonNode;
        } catch (JsonProcessingException e) {
            WebLogger.Instance.error(e);
            throw new RuntimeException(e);
        }
    }

    public <T> T jsonToValue(JsonNode json, Class<T> valueType) {
        ObjectMapper mapper = this.getDefaultObjectMapper();
        return mapper.convertValue(json, valueType);
    }

    public JsonNode valueToJson(Object metadataContent, PropertyNamingStrategy strategy) {
        ObjectMapper mapper = this.getDefaultObjectMapper();
        PropertyNamingStrategy originalStrategy = mapper.getPropertyNamingStrategy();
        boolean changeStrategy = !originalStrategy.equals(strategy);
        if (changeStrategy) {
            mapper.setPropertyNamingStrategy(strategy);
        }

        JsonNode result = mapper.valueToTree(metadataContent);

        if (changeStrategy) {
            mapper.setPropertyNamingStrategy(originalStrategy);
        }
        return result;
    }

    public JsonNode valueToJson(Object metadataContent) {
        return valueToJson(metadataContent, PropertyNamingStrategy.LOWER_CAMEL_CASE);
    }

    // endregion
}
